/*---------------------------------------------------------------------------*\
  =========                 |
  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
   \\    /   O peration     |
    \\  /    A nd           | Copyright held by original author
     \\/     M anipulation  |
-------------------------------------------------------------------------------
License
    This file is part of OpenFOAM.

    OpenFOAM is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the
    Free Software Foundation; either version 2 of the License, or (at your
    option) any later version.

    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    for more details.

    You should have received a copy of the GNU General Public License
    along with OpenFAM; if not, write to the Free Software Foundation,
    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

Application
    foamMeshToAbaqus

Description
    Converts an OpenFOAM mesh to an Abaqus input file.
    Creates a node set and and element set and a surface for each boundary
    patch.
    Also creates a element set for each material in the materials file (if
    it is exists).
    Only works for hexahedral cells as yet.
    Checked for Abaqus-6.9-2.

Author
    philip.cardiff@ucd.ie

\*---------------------------------------------------------------------------*/

#include "fvCFD.H"
#include "OFstream.H"

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

int main(int argc, char *argv[])
{
#   include "setRootCase.H"
#   include "createTime.H"
#   include "createMesh.H"

  //----------------------------------------------------------------------//
  //- open abaqus input file
  //----------------------------------------------------------------------//
  Info << "Opening Abaqus input file" << endl << endl;
  OFstream abaqusInp("abaqusMesh.inp");

  //- input header
  Info << "Writing input file header" << endl << endl;
  abaqusInp << "*Heading\n"
	    << "** Job name: OpenFoamMesh Model name: OpenFoamMesh\n"
	    << "** Generated by: Abaqus/CAE 6.9-2\n"
	    << "*Preprint, echo=NO, model=NO, history=NO, contact=NO\n"
	    << "**\n"
	    << "** PARTS\n"
	    << "**\n"
	    << "*Part, name=OpenFoamMeshPart"
	    << endl;


  //----------------------------------------------------------------------//
  //- write nodes
  //----------------------------------------------------------------------//
  Info << "Writing Nodes" << endl << endl;
  abaqusInp << "*Node" << endl;
  const pointField& points = mesh.points();
  forAll(points, pointi)
    {
      abaqusInp << "\t" << (pointi+1)
		<< ",\t" << (points[pointi]).x()
		<< ",\t" << (points[pointi]).y()
		<< ",\t" << (points[pointi]).z()
		<< endl;
    }


  //----------------------------------------------------------------------//
  //- determine abaqusCellPoints
  //----------------------------------------------------------------------//
  //- for hex 1st order elements, abaqus orders the points
  //- where nodes 1 2 3 4 are clockwise from the outside of the cell
  //- and then 5 6 7 8 are apposite 1 2 3 4 respectively
  Info << "Determining Abaqus element node ordering" << endl << endl;

  const cellList& cells = mesh.cells();
  const faceList& faces = mesh.faces();
  const labelListList pointPoints = mesh.pointPoints();
  const labelListList cellPoints = mesh.cellPoints();
  const labelList faceOwner = mesh.faceOwner();
  const vectorField faceNormals = mesh.Sf()/mesh.magSf();

  labelListList abaqusCellPoints(cellPoints.size(), List<label>(8, 1));

  forAll(cells, celli)
    {
      //- face0 will be our reference face
      //- face0 seems to be always owned by the cell
      //- so the face normal points out of the cell
      label refFace = cells[celli][0];

      //- insert first four abaqusCellPoints
      abaqusCellPoints[celli][0]
	= (faces[refFace][3] + 1);
      abaqusCellPoints[celli][1]
	= (faces[refFace][2] + 1);
      abaqusCellPoints[celli][2]
	= (faces[refFace][1] + 1);
      abaqusCellPoints[celli][3]
	= (faces[refFace][0] + 1);

      //- now find the opposite face in the cell
      //Info << "Finding oppFace" << endl << endl;
      labelList refFacePoints = faces[refFace];
      //- compare each faces points to the refFace, the opposite
      //- face will share points with the refFace
      label oppFace = -1;

      //- for all the cell faces
      forAll(cells[celli], facei)
	{
	  bool faceHasNoCommonPoints = true;
	  label globalFaceLabel = cells[celli][facei];

	  //- for all the face points
	  forAll(faces[globalFaceLabel], pointi)
	    {
	      label globalPointLabel = faces[globalFaceLabel][pointi];
 
	      //- compare each point with all the refFace points
	      forAll(faces[refFace], refFacePointi)
		{
		  label refFaceGlobalPointLabel = faces[refFace][refFacePointi];
		  if(globalPointLabel == refFaceGlobalPointLabel)
		    {
		      faceHasNoCommonPoints = false;
		    }
		}
	    }
	  if(faceHasNoCommonPoints)
	    {
	      oppFace = globalFaceLabel;
	      break;
	    }
	}
      if(oppFace == -1)
	{
	  SeriousError << "\n\nCannot find face opposite reference face,"
		       << " are you sure this is a hex mesh?"
		       << endl
		       << exit(FatalError);
	}


      //- Now find out the which point on oppFace is attached
      //- to each point of the refFace
      
      //- for all the oppFace points
      forAll(faces[oppFace], pointi)
	{
	  label globalPointi = faces[oppFace][pointi];

	  //- get the oppFace pointPoints
	  const labelList& oppFacePPs = pointPoints[globalPointi];

	  bool ppFound = false;

	  //- for all the oppFace point points
	  forAll(oppFacePPs, oppFacePointi)
	    {
	      label globalPpi = oppFacePPs[oppFacePointi];
	      if(globalPpi == faces[refFace][0])
		{
		  abaqusCellPoints[celli][7]
		    = globalPointi + 1;
		  ppFound = true;
		  break;
		}
	      else if(globalPpi == faces[refFace][1])
		{
		  abaqusCellPoints[celli][6]
		    = globalPointi + 1;
		  ppFound = true;
		  break;
		}
	      else if(globalPpi == faces[refFace][2])
		{
		  abaqusCellPoints[celli][5]
		    = globalPointi + 1;
		  ppFound = true;
		  break;
		}
	      else if(globalPpi == faces[refFace][3])
		{
		  abaqusCellPoints[celli][4]
		    = globalPointi + 1;
		  ppFound = true;
		  break;
		}
	    }
	  if(!ppFound)
	    {
	      SeriousError << "\n\nCannot find point point opposite reference face points,"
			   << " some thing strange is happening..."
			   << endl
			   << exit(FatalError);
	    }
	}
    }


  //----------------------------------------------------------------------//
  //- Writing elements
  //----------------------------------------------------------------------//
  Info << "Writing Elements" << endl << endl;
  abaqusInp << "*Element, type=C3D8R" << endl;
  forAll(cells, celli)
    {
      //- print cell number
      abaqusInp << " " << (celli+1);

      //- print points on the first face
      forAll(abaqusCellPoints[celli], pointi)
	{
	  abaqusInp << ", " << (abaqusCellPoints[celli][pointi]);
	}
      abaqusInp << endl;
    }


  //----------------------------------------------------------------------//
  //- Writing node sets for each patch
  //----------------------------------------------------------------------//
  Info << "Writing node sets for each boundary patch" << endl;
  forAll(mesh.boundaryMesh(), patchi)
    {
      Info << "\tWriting points on patch " << mesh.boundaryMesh()[patchi].name()
	   << " to nodeSet" << mesh.boundaryMesh()[patchi].name() << endl;

      //- Node set name
      abaqusInp << "*Nset, nset=nodeSet" << mesh.boundaryMesh()[patchi].name() << endl;
      
      //- Node labels
      forAll(mesh.boundaryMesh()[patchi].meshPoints(), pointi)
	{
	  label globalPointi = mesh.boundaryMesh()[patchi].meshPoints()[pointi];
	  abaqusInp << globalPointi + 1;
	  //- a comma after every label except the last
	  if( pointi != (mesh.boundaryMesh()[patchi].meshPoints().size()-1) )
	    {
	      abaqusInp << ", ";
	    }
	  else
	    {
	      abaqusInp << endl;
	    }

	  //- put 10 labels on each line for tidiness
	  if(pointi % 10 == 0 && pointi != 0 && pointi != (mesh.boundaryMesh()[patchi].meshPoints().size()-1))
	    {
	      abaqusInp << endl;
	    }
	}
    }
  Info << endl;


  //----------------------------------------------------------------------//
  //- Writing element sets for each patch
  //----------------------------------------------------------------------//
  Info << "Writing element sets for each boundary patch" << endl;
  forAll(mesh.boundaryMesh(), patchi)
    {
      Info << "\tWriting cells on patch " << mesh.boundaryMesh()[patchi].name()
	   << " to elementSet" << mesh.boundaryMesh()[patchi].name() << endl;

      //- Element set name
      abaqusInp << "*Elset, elset=elementSet" << mesh.boundaryMesh()[patchi].name() << endl;
      
      //- Element labels
      forAll(mesh.boundaryMesh()[patchi].faceCells(), celli)
	{
	  label globalCelli = mesh.boundaryMesh()[patchi].faceCells()[celli];
	  abaqusInp << globalCelli + 1;
	  //- a comma after every label except the last
	  if( celli != (mesh.boundaryMesh()[patchi].size()-1) )
	    {
	      abaqusInp << ", ";
	    }
	  else
	    {
	      abaqusInp << endl;
	    }

	  //- put 10 labels on each line for tidiness
	  if(celli % 10 == 0 && celli != 0 && celli != (mesh.boundaryMesh()[patchi].size()-1))
	    {
	      abaqusInp << endl;
	    }
	}
    }
  Info << endl;


  //----------------------------------------------------------------------//
  //- Writing element set for each material
  //----------------------------------------------------------------------//
  IOobject materialsHeader
    (
     "materials",
     runTime.timeName(),
     mesh,
     IOobject::MUST_READ
     );

  // Check U exists                                                                                                                             
  if (materialsHeader.headerOk())
    {
      Info << "Reading materials field and writing each material to an element set"
	   << endl;
      
      volScalarField materials(materialsHeader, mesh);
      const scalarField& materialsI = materials.internalField();
      
      //- order the material into groups and record their cell numbers
      label numberOfMaterials = round(max(materials).value()) + 1;
      labelList cellsOfEachMaterial(numberOfMaterials,0);
      forAll(materialsI, celli)
	{
	  label matNum = materialsI[celli];
	  forAll(cellsOfEachMaterial, mati)
	    {
	      if(matNum == mati)
		{
		  cellsOfEachMaterial[mati]++;
		}
	    }
	}

      labelListList materialsOrdered(numberOfMaterials, List<label>(0,0));
      forAll(materialsOrdered, matOrderi)
	{
	  materialsOrdered[matOrderi].setSize(cellsOfEachMaterial[matOrderi], 0);
	  label mc = 0;
	  forAll(materialsI, celli)
	    {
	      label matNum = materialsI[celli];
	      if(matNum == matOrderi)
		{
		  materialsOrdered[matOrderi][mc] = celli;
		  mc++;
		}
	    }

	  Info << "\tWriting " << cellsOfEachMaterial[matOrderi] << " cells of material "
	       << matOrderi << " to elementSetMaterial" << matOrderi << endl;
	  abaqusInp << "*Elset, elset=elementSetMaterial" << matOrderi << endl;
	  forAll(materialsOrdered[matOrderi], celli)
	    {
	      abaqusInp << (materialsOrdered[matOrderi][celli]+1);
	      if(celli != materialsOrdered[matOrderi].size()-1)
		{
		  abaqusInp << "," << endl;
		}
	      else
		{
		  abaqusInp << endl;
		}
	    }
	}
    }
  else
    {
      Info << "No materials file present so no material element sets will be written"
	   << endl << endl;
    }
  Info << endl;


  //----------------------------------------------------------------------//
  //- Writing surfaces for each patch
  //----------------------------------------------------------------------//
  Info << "Writing surfaces for each boundary patch" << endl;
  forAll(mesh.boundaryMesh(), patchi)
    {
      Info << "\tWriting cells on patch " << mesh.boundaryMesh()[patchi].name()
	   << " to surface" << mesh.boundaryMesh()[patchi].name() << endl;

      //- Surface name
      abaqusInp << "*Surface, type=ELEMENT, name=surface" << mesh.boundaryMesh()[patchi].name() << endl;

      //- the face is denoted by either s1, s2, s3, s4, s5 or s6
      //- so I have to find the exterior face of the element and
      //- the corresponding s number
      //- face convention
      //- face 1 is nodes 1 2 3 4
      //- face 2 is nodes 5 8 7 6
      //- face 3 is nodes 1 5 6 2
      //- face 4 is nodes 2 6 7 3
      //- face 5 is nodes 3 7 8 4
      //- face 6 is nodes 4 8 5 1
      //- So i will compare points on the face and
      labelListList abaqusFaceConvention(6, List<label>(4,-1));
      abaqusFaceConvention[0][0] = 1;
      abaqusFaceConvention[0][1] = 2;
      abaqusFaceConvention[0][2] = 3;
      abaqusFaceConvention[0][3] = 4;
      abaqusFaceConvention[1][0] = 5;
      abaqusFaceConvention[1][1] = 8;
      abaqusFaceConvention[1][2] = 7;
      abaqusFaceConvention[1][3] = 6;
      abaqusFaceConvention[2][0] = 1;
      abaqusFaceConvention[2][1] = 5;
      abaqusFaceConvention[2][2] = 6;
      abaqusFaceConvention[2][3] = 2;
      abaqusFaceConvention[3][0] = 2;
      abaqusFaceConvention[3][1] = 6;
      abaqusFaceConvention[3][2] = 7;
      abaqusFaceConvention[3][3] = 3;
      abaqusFaceConvention[4][0] = 3;
      abaqusFaceConvention[4][1] = 7;
      abaqusFaceConvention[4][2] = 8;
      abaqusFaceConvention[4][3] = 4;
      abaqusFaceConvention[5][0] = 4;
      abaqusFaceConvention[5][1] = 8;
      abaqusFaceConvention[5][2] = 5;
      abaqusFaceConvention[5][3] = 1;
      
      //- Element labels and the free face label
      forAll(mesh.boundaryMesh()[patchi], facei)
	{
	  label globalCelli = mesh.boundaryMesh()[patchi].faceCells()[facei];
	  abaqusInp << (globalCelli + 1) << ", ";

	  //- compare this face's points with the abaqusCellPoints and see which points
	  //- are used, then compared the points used to the faces above to see which
	  //- face it is
	  const labelList& facePoints = mesh.boundaryMesh()[patchi][facei];
	  const labelList& thisCellAbaqusPoints = abaqusCellPoints[globalCelli];
	  labelList thisFaceAbaqusPoints(4,-1);
	  label pointsFound = 0;
	  forAll(thisCellAbaqusPoints, pointi)
	    {
	      label cellPointi = thisCellAbaqusPoints[pointi];
	      if(cellPointi == facePoints[0]+1)
		{
		  pointsFound++;
		  thisFaceAbaqusPoints[0] = pointi+1;
		}
	      else if(cellPointi == facePoints[1]+1)
		{
		  pointsFound++;
		  thisFaceAbaqusPoints[1] = pointi+1;
		}
	      else if(cellPointi == facePoints[2]+1)
		{
		  pointsFound++;
		  thisFaceAbaqusPoints[2] = pointi+1;
		}
	      else if(cellPointi == facePoints[3]+1)
		{
		  pointsFound++;
		  thisFaceAbaqusPoints[3] = pointi+1;
		}
	    }
	  if(pointsFound != 4)
	    {
	      SeriousError << "Something is wrong while determing the surfaces from the patches"
			   << endl << exit(FatalError);
	    }

	  //- now thisFaceAbaqusPoints holds the points on the face so
	  //- compare this face to face[1-6] to see which face it is.
	  //- The points won't neccesarily be in the same order as faces[1-6]
	  //- but will contain the same labels
	  forAll(abaqusFaceConvention, abqfacei)
	    {
	      int pointsOnFace = 0;
	      forAll(abaqusFaceConvention[abqfacei], pointi)
		{
		  forAll(thisFaceAbaqusPoints, pi)
		    {
		      if(thisFaceAbaqusPoints[pi] == abaqusFaceConvention[abqfacei][pointi])
			{
			  pointsOnFace++;
			}
		    }
		}
	      if(pointsOnFace == 4)
		{
		  //Info << "found face!" << endl;
		  abaqusInp << " s" << (abqfacei + 1) << endl;
		}
	    }
	}
    }
  Info << endl;


  //----------------------------------------------------------------------//
  //- end part
  //----------------------------------------------------------------------//
  abaqusInp << "*End Part" << endl;

  Info << "Finished writing abaqusMesh.inp" << endl << endl;

  return(0);
}


// ************************************************************************* //
